package com.coincalf.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.coincalf.dto.CoinInfoDto;
import com.coincalf.dto.HzDTO;
import com.coincalf.entity.*;
import com.coincalf.framework.enums.AmountDirection;
import com.coincalf.framework.enums.BusinessType;
import com.coincalf.framework.exception.AccountException;
import com.coincalf.framework.exception.GlobalDefaultException;
import com.coincalf.framework.http.Response;
import com.coincalf.framework.utils.HttpClient;
import com.coincalf.mapper.HzRecordMapper;
import com.coincalf.service.*;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class HzRecordServiceImpl extends ServiceImpl<HzRecordMapper, HzRecord> implements HzRecordService {

    @Autowired
    private UserService userService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private CoinService coinService;
    @Autowired
    private AccountDetailService accountDetailService;

    @Value("${php.hz.url}")
    private String phpHzUrl;
    @Value("${php.sign.key}")
    private String phpSignKey;

    @Override
    public Map<String, String> executeHzAndNotice(HzDTO hzDTO) {
        Map<String, String> map = new HashMap<>(4);
        User user = userService.selectByPhone(hzDTO.getPhone());
        if (user == null) {
            map.put("code", "102");
            map.put("message", "用户不存在");
            return map;
        }

        HzRecord hz = super.selectOne(new EntityWrapper<HzRecord>().eq("trade_no", hzDTO.getTrade_no()));
        if (hz != null) {
            map.put("code", "419");
            map.put("message", "HAS");
            return map;
        }

        String name = hzDTO.getName();
        if (name.equals("USDT")) {
            name = "USDT_OMNI";
        }
        CoinInfoDto coinInfoDto = coinService.queryInfo(name);

        //增加用户可用余额
        Account account = accountService.queryByUserIdAndCoinId(user.getId(), coinInfoDto.getCoinId());
        if (account == null) {
            log.error("增加资金-资金账户异常，userId:{}, coinId:{}", user.getId(), coinInfoDto.getCoinId());
            map.put("code", "103");
            map.put("message", "用户资金账户不存在");
            return map;
        }

        BigDecimal num = new BigDecimal(hzDTO.getNum());
        if (accountService.addAmountById(account.getId(), num) > 0) {
            // 保存流水
            AccountDetail accountDetail = new AccountDetail(
                    user.getId(),
                    coinInfoDto.getCoinId(),
                    account.getId(),
                    account.getId(),
                    System.currentTimeMillis(),
                    AmountDirection.INCOME.getType(),
                    BusinessType.HZ_MINE_TO_EX.getCode(),
                    num,
                    BusinessType.HZ_MINE_TO_EX.getDesc()
            );
            accountDetailService.insert(accountDetail);

            //划转纪录
            HzRecord hzRecord = this.hzDto2HzRecord(hzDTO);
            super.insert(hzRecord);

            map.put("code", "200");
            map.put("message", "操作成功");
            return map;
        }

        map.put("code", "500");
        map.put("message", "操作失败");
        return map;
    }

    @Override
    public Response executeHz(HzDTO hzDTO, User user) {
        //coinId校验
        Coin coin = coinService.selectById(hzDTO.getCoinId());
        if (!(coin.getName().equals("USDT_ERC20") || coin.getName().equals("USDT_OMNI")
                || coin.getName().equals("AQS") || coin.getName().equals("AQP"))) {
            return Response.err(-55, "暂不支持该币种划转");
        }
        if (coin.getName().startsWith("USDT")) {
            hzDTO.setName("USDT");
        } else {
            hzDTO.setName(coin.getName());
        }
        //查看余额是否足够
        EntityWrapper<Account> wrap = new EntityWrapper<>();
        wrap.eq("user_id", user.getId())
                .eq("coin_id", hzDTO.getCoinId());
        Account account = accountService.selectOne(wrap);
        if (account == null) {
            log.error("资金划转-资金账户异常，userId:{}, coinId:{}", user.getId(), hzDTO.getCoinId());
            throw new AccountException("资金账户不存在");
        }
        BigDecimal amount = new BigDecimal(hzDTO.getNum());
        if (account.getBalanceAmount().compareTo(amount) < 0) {
            log.warn("划转可用资金不足 amount:{} balance:{}", amount, account.getBalanceAmount());
            throw new GlobalDefaultException(50027);
        }
        //调用PHP接口 成功则扣除用户余额
        hzDTO.setFrom("EX");
        hzDTO.setTo("MINE");
        hzDTO.setTrade_no(UUID.randomUUID().toString().replaceAll("-", "").toLowerCase());
        hzDTO.setPhone_code(user.getCountryCode());
        hzDTO.setPhone(user.getMobile());

        //生成签名
        Map<String, String> map = new HashMap<>(4);
        map.put("time", String.valueOf(System.currentTimeMillis()));
        map.put("nonce", UUID.randomUUID().toString());
        map.put("equip_uuid", UUID.randomUUID().toString());
        String signData = getStrByParameterJson(map);
        byte[] signDataBytes = signData.getBytes();
        String sign = HzRecordServiceImpl.sign(signDataBytes, this.phpSignKey);
        hzDTO.setSign(sign);
        map.put("sign", sign);
        map.put("from", "EX");
        map.put("to", "MINE");
        map.put("num", hzDTO.getNum());
        map.put("name", hzDTO.getName());
        map.put("trade_no", hzDTO.getTrade_no());
        map.put("phone", hzDTO.getPhone());
        map.put("phone_code", hzDTO.getPhone_code());
        //String jsonStr = JsonUtils.writeValueAsString(hzDTO);
        //String result = HttpUtil.doPost(this.phpHzUrl, jsonStr);
        String result = HttpClient.post(this.phpHzUrl, map, "");
        boolean success;
        try {
            JSONObject jsonObject = JSONObject.parseObject(result);
            String message = jsonObject.getString("message");
            String code = jsonObject.getString("code");
            log.info("EX->MINE调用PHP接口返回值code：{}  msg：{}", code, message);
            success = "200".equals(code);
        } catch (Exception e) {
            log.warn("调用PHP接口失败-交易所划转到矿机系统失败");
            return Response.err(-88, "操作失败");
        }
        if (success) {
            int i = accountService.subAmountById(account.getId(), amount);
            if (i > 0) {
                // 保存流水
                AccountDetail accountDetail = new AccountDetail(
                        user.getId(),
                        hzDTO.getCoinId(),
                        account.getId(),
                        account.getId(),
                        System.currentTimeMillis(),
                        AmountDirection.OUT.getType(),
                        BusinessType.HZ_EX_TO_MINE.getCode(),
                        amount,
                        BusinessType.HZ_EX_TO_MINE.getDesc()
                );
                accountDetailService.insert(accountDetail);

                HzRecord hzRecord = this.hzDto2HzRecord(hzDTO);
                super.insert(hzRecord);

                return Response.ok();
            } else {
                log.warn("PHP操作成功，JAVA扣除用户资金失败 accountId={} amount={}", account.getId(), amount);
                return Response.err(-77, "操作失败");
            }
        } else {
            log.warn("调用PHP接口失败-交易所划转到矿机系统失败");
            return Response.err(-88, "操作失败");
        }
    }

    @Override
    public Response list(Integer pageNo, Integer pageSize, String name, Integer hzDirection, String userMobile) {
        Page<HzRecord> page = new Page<>();
        page.setCurrent(pageNo - 1).setSize(pageSize);
        EntityWrapper<HzRecord> wrap = new EntityWrapper<>();
        if (!StringUtils.isEmpty(name)) {
            wrap.eq("name", name);
        }
        if (hzDirection != null) {
            wrap.eq("hz_direction", hzDirection);
        }
        wrap.orderBy("created", false);
        Page<HzRecord> result = this.selectPage(page, wrap);
        return Response.ok(result);
    }

    private HzRecord hzDto2HzRecord(HzDTO hzDTO) {
        HzRecord hz = new HzRecord();
        hz.setFrom(hzDTO.getFrom());
        hz.setTo(hzDTO.getTo());
        hz.setCountryCode(hzDTO.getPhone_code());
        hz.setMobile(hzDTO.getPhone());
        hz.setName(hzDTO.getName());
        hz.setNum(new BigDecimal(hzDTO.getNum()));
        hz.setTradeNo(hzDTO.getTrade_no());
        if (hzDTO.getFrom().equals("MINE")) {
            hz.setHzDirection(2);
        } else {
            hz.setHzDirection(1);
        }
        hz.setCreated(new Date());
        return hz;
    }

    public static void main(String[] args) throws Exception {
        Map<String, String> map = new HashMap<>(4);
        map.put("time", "1612608763000");
        map.put("nonce", "69408037");
        map.put("equip_uuid", "4D5BEED5-7349-4CCF-B954-FFA0B09C0FEC");
        String inputStr = getStrByParameterJson(map);
        byte[] inputData = inputStr.getBytes();
        String key = "9ec254da-ca69-5296-9d40-080766c652e5";
        System.out.println(HzRecordServiceImpl.sign(inputData, key));


        String url = "http://localhost:8080/v2/w/withdraw/record";
        Map<String, String> map2 = new HashMap<>(4);
        map2.put("current", "100");
        map2.put("size", "20");
        map2.put("coinId", "2");

        String post = HttpClient.post(url, map2, "");
        System.out.println(post);

    }

    /**
     * 参数排序并转字符串
     */
    private static String getStrByParameterJson(Map<String, String> map, String... exclude) {
        if (map != null) {
            // 排除
            for (String key : exclude) {
                map.remove(key);
            }
            // 遍历
            StringBuilder sb = new StringBuilder();
            Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
            boolean hasNext = iterator.hasNext();
            while (hasNext) {
                Map.Entry<String, String> entry = iterator.next();
                Object value = entry.getValue();
                if (value != null && org.apache.commons.lang3.StringUtils.isNotBlank(value.toString())) {
                    sb.append(entry.getKey()).append('=').append(value);
                }
                if (hasNext = iterator.hasNext()) {
                    sb.append('&');
                }
            }
            return sb.toString();
        }
        return null;
    }

    private static String sign(byte[] dataBytes, String key) {
        SecretKey secretKey = new SecretKeySpec(key.getBytes(), "HmacMD5");
        Mac mac;
        try {
            mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
        } catch (Exception e) {
            log.warn("数据加密异常 e={}", e.getMessage());
            return null;
        }
        byte[] bytes = mac.doFinal(dataBytes);
        return Hex.toHexString(bytes);
    }

}
